#include "biped_instance.h"

#include "script/scripthelper.h"
#include "dd/dd_man.h"
#include "font/font_manager.h"
#include "math/facing.h"
#include "math/intersection.h"
//#include "math/rect.h"
#include "graphics/graphics_util.h"
#include "vfs/vfs.h"
#include "num/rand.h"

#include "util/timer.h"				// log
#include "util/log.h"				// log
#include "util/common_macros.h"		// macros
#include "util/mmgr.h"		    

#include <cassert>
#include <algorithm>
using namespace bones;
using namespace script;

float	BipedInstance::XBLEND_TIME = 5.0f;	// in frames

unsigned int		BipedInstance::BIPEDS_DRAWN= 0;
unsigned int		BipedInstance::BONES_DRAWN = 0;		
bool				BipedInstance::DRAW_BONES = false;


BipedInstance::BipedInstance() :
	m_AnimTime(0),
	m_pAnimation(0),
	m_pBlendAnimation(0),
	m_Flipped(false),
	m_ReverseAttachedBiped(false),
	m_ForceSpriteReload(false),
	m_WrapAnimation(true),
	m_FrameID(0)
{
	m_Pos = math::Vec2(0.5f, 0.9f);

	OBJECT_SCALE = 1;
	FRAME_SPEED = 30;
	DRAW_BONES = false;
	DEFAULT_PRECAT = "";
	OBJECT_COLOR = -1;
	IS_ADDITIVE = false;
	SHADOW_SCALE = -1;
	//SetAnimation("walk");
}

BipedInstance::~BipedInstance()
{
	Clear();
	ClearSprites();
}


void	BipedInstance::FlushImages()
{
// note that we're not actually deleting the sprites here, just ditching the resources
	for (unsigned int i=0; i<m_Bones.size(); i++)
	{
		BoneData*	pData = m_Bones[i];
#ifdef BONES_SINGLE_LAYER
			pData->Sprite.FlushImage();
#else
		for (unsigned int j=0; j<pData->Sprites.size(); j++)
		{
			pData->Sprites[j]->FlushImage();
		}
#endif
	}
}

void	BipedInstance::ResetAnimation()
{
	m_FrameID = 0;
	m_AnimTime = m_BlendTime = 0;
}
void	BipedInstance::Clear()
{
	m_CurrentAnim.resize(0);
	m_NextAnim.resize(0);
	m_pAnimation = NULL;
	m_pBlendAnimation = NULL;
	for (unsigned int i=0; i<m_Bones.size(); i++)
	{
		delete m_Bones[i];
	}
	m_Bones.resize(0);

}

void	BipedInstance::GetExtents(math::Vec2& Min, math::Vec2& Max)
{
	if (m_pAnimation)
	{
		Min = 	m_pAnimation->Min;
		Max= 	m_pAnimation->Max;
	}
}

bool	BipedInstance::GetSpriteExtents(math::Vec2& Min, math::Vec2& Max)
{
	// can we cache these?
	bool HasSprites = false;
	Min = math::Vec2(1000.0f, 1000.0f);
	Max = math::Vec2(-1000.0, -1000.0f);

	for (int i = 0; i < (int)m_Bones.size(); i++)
	{
		BipedInstance::BoneData* pBone = m_Bones[i];
#ifdef BONES_SINGLE_LAYER
		if (pBone->Sprite.IsValid())
		{
			math::Vec2	BonePos, Dir, P;
			float S;
			GetBoneData(pBone, BonePos, Dir);
			P = BonePos + (Dir * pBone->Sprite.m_Size.y * OBJECT_SCALE);
			S = 0.25f * (pBone->Sprite.m_Size.x + pBone->Sprite.m_Size.y);

			Min.x = UTIL_LOW(Min.x, UTIL_LOW(P.x - S, BonePos.x));
			Min.y = UTIL_LOW(Min.y, UTIL_LOW(P.y - S, BonePos.y));
			Max.x = UTIL_HIGH(Max.x, UTIL_HIGH(P.x + S, BonePos.x));
			Max.y = UTIL_HIGH(Max.y, UTIL_HIGH(P.y + S, BonePos.y));
			HasSprites = true;

		}
#else
		if (!pBone->NoValidSprites())
		{
			math::Vec2	BonePos, Dir, P;
			float S;
			GetBoneData(pBone, BonePos, Dir);
			for (int j = 0; j < (int)pBone->Sprites.size(); j++)
			{
				ui::UISprite* pSprite = pBone->Sprites[j];
				if (!pSprite->IsValid()) continue;

				P = BonePos + (Dir * pSprite->m_Size.y * OBJECT_SCALE);
				S = 0.25f * (pSprite->m_Size.x + pSprite->m_Size.y);

				Min.x = UTIL_LOW(Min.x, UTIL_LOW(P.x - S, BonePos.x));
				Min.y = UTIL_LOW(Min.y, UTIL_LOW(P.y - S, BonePos.y));
				Max.x = UTIL_HIGH(Max.x, UTIL_HIGH(P.x + S, BonePos.x));
				Max.y = UTIL_HIGH(Max.y, UTIL_HIGH(P.y + S, BonePos.y));
				HasSprites = true;
			}
		}
#endif
	}

	if (!HasSprites) GetExtents(Min, Max); // just fall back to the normal GetExtents
	return HasSprites;
}

bool	BipedInstance::Tick(float dt)
{
	if (!m_pAnimation) return true;

	int	LastLoopC = int(m_AnimTime/m_pAnimation->Frames);
	int	OldFrame = int(m_AnimTime)  % m_pAnimation->Frames;
	m_AnimTime+=dt* FRAME_SPEED;
	int	CurrentFrame = int(m_AnimTime)  % m_pAnimation->Frames;
	m_FrameID = CurrentFrame;	// in case anyone else wants to know
	int	LoopC = int(m_AnimTime/m_pAnimation->Frames);

	// update animating sprites
	for (unsigned int i=0; i<m_Bones.size(); i++)
	{
		BoneData*	pBone = m_Bones[i];
#ifdef BONES_SINGLE_LAYER
		pBone->Sprite.Tick(dt);
#else
		for (unsigned int j=0; j<pBone->Sprites.size(); j++)
			pBone->Sprites[j]->Tick(dt);
#endif //BONES_SINGLE_LAYER

	}
	// do this conditional outside the inner loop; it's relatively rare.
	if (OldFrame != CurrentFrame)
	{
		// new frame.. do anything usefull?
		// do this only for the original animation or for the blend anim as well?
	}

	if (LoopC != LastLoopC && m_CallbackA[CB_ANIM_COMPLETE].pCB)	
	{
		m_CallbackA[CB_ANIM_COMPLETE].pCB(this, m_pAnimation, m_CallbackA[CB_ANIM_COMPLETE].pCBData);
	}
	if (m_pBlendAnimation)
	{
		m_BlendTime+=dt* FRAME_SPEED;
		if (m_BlendTime >= XBLEND_TIME)
		{
			m_pAnimation = m_pBlendAnimation;
			m_CurrentAnim = m_NextAnim;
			m_NextAnim.resize(0);
			m_AnimTime = m_BlendTime;
			m_BlendTime = 0;	
			m_pBlendAnimation = NULL;
			RefreshBones();
			// some cases should also reload sprites at this point
			if (m_ForceSpriteReload)
			{
				RefreshBones();
				LoadDefaults();
				m_ForceSpriteReload = false;
			}
			if (m_CallbackA[CB_TRANSITION_COMPLETE].pCB)
			{
				m_CallbackA[CB_TRANSITION_COMPLETE].pCB(this, m_pAnimation, m_CallbackA[CB_TRANSITION_COMPLETE].pCBData);
			}
		}
	} 	
	return (OldFrame <= CurrentFrame);
}

void	BipedInstance::SetFlipped(bool b)
{
	if (b!=m_Flipped)
	{
		m_Flipped = b;
		ResortBones();
	}
}

float	BipedInstance::GetAnimationTime()
{
	if (m_pBlendAnimation)
	{
		return m_pBlendAnimation->RunTime;
	} else
	if (m_pAnimation)
	{
		return m_pAnimation->RunTime;
	} 

	return 1.0f;
}

void	BipedInstance::SetNextAnim(const std::string& Name, bool Force)	
{ 
	if (!Force)
	{
		if (strcmpi(m_NextAnim.c_str(), Name.c_str())==0) return;		// already set
		if (strcmpi(Name.c_str(), m_CurrentAnim.c_str())==0) return;	// same as current
	}
	if (m_pAnimation)
	{
		m_NextAnim = Name; 
		m_BlendTime = 0;	
		m_pBlendAnimation = bones::BipedAnimationSet::Instance().GetAnimation(vfs::VFS::GetBaseName(Name));
	} else
	{
		SetAnimation(Name);
	}
}


bool	BipedInstance::GetBonePos(BoneData* pBone, math::Vec2& From, math::Vec2& To, math::Vec2& Dir, float& Rotation)
{
	if (!pBone) return false;
	if (!pBone->pBone) return false;

	float Length = pBone->pBone->Length;

#ifdef BONES_SINGLE_LAYER
	math::Vec2	Size;
	pBone->Sprite.GetScreenSize(Size);
	Length = Size.y;
#else
	if (!pBone->NoValidSprites())
	{
		math::Vec2	Size;
		pBone->Sprites[0]->GetScreenSize(Size);
		Length = Size.y;
	}
#endif //BONES_SINGLE_LAYER

	pBone->pBone->GetBlendedFrame(m_AnimTime,  From, Rotation, true); 
	Dir = math::Facing::VectorFromAngle(Rotation);
	if (IsFlipped())
	{
		From.x *=-1;
		Dir.x *=-1;
	} 

	To.x = From.x + (Dir.x * Length);
	To.y = From.y + (Dir.y * Length);
	return true;
}

bool	BipedInstance::GetRawBonePos(const std::string& Name, math::Vec2* pFrom, math::Vec2* pTo)
{
	BoneData* pData = GetBone(Name);
	if (!pData) return false;
	return GetRawBonePos(pData, pFrom, pTo);
}

bool	BipedInstance::GetRawBonePos(BoneData* pBone,  math::Vec2* pFrom, math::Vec2* pTo)
{
	if (!pBone) return false;
	if (!pFrom) return false;

	if (!pBone->pBone) return false;

	math::Vec2 BonePos, Dir;
	GetBoneData(pBone, BonePos, Dir);

	*pFrom = BonePos;

	if (pTo)
	{
		*pTo = *pFrom + (Dir * pBone->pBone->Length);
	}
	return true;
}


bool	BipedInstance::IsInside(const math::Vec2& Pos)
{
	math::Vec2	P = Pos - m_Pos;	// local coordinates
	// quick cull first
	math::Vec2	Min, Max;
	GetExtents(Min, Max);
	if (UTIL_RANGE(P.x, Min.x, Max.x) && 
		UTIL_RANGE(P.y, Min.y, Max.y))
	{
		for (unsigned int i=0; i<m_Bones.size(); i++)
		{
			BoneData* pData = m_Bones[i];
			if (!pData->pBone) continue;

			math::Vec2 BonePos, Dir;
			GetBoneData(pData, BonePos, Dir);
			math::Vec2	To = BonePos + (Dir * pData->pBone->Length);

			math::Vec2	Normal;
			float		Penetration;
			if (math::Intersection::SphereCapsule2D(P, 10*GraphicsUtil::InvW, BonePos, To, 10*GraphicsUtil::InvW,Penetration, Normal))
			{
				return true;
			}
		}
	}
	return false;
}

bool	BipedInstance::IsInsideSprite(const math::Vec2& Pos)
{
	math::Vec2	P = Pos - m_Pos;	// local coordinates

	for (int i = 0; i < (int)m_Bones.size(); i++)
	{
		BipedInstance::BoneData* pBone = m_Bones[i];
#ifdef BONES_SINGLE_LAYER
		if (pBone->Sprite.IsValid())
		{
			math::Vec2	BonePos, Dir;
			GetBoneData(pBone, BonePos, Dir);
			ui::UISprite*	pSprite = &pBone->Sprite;
			math::Vec2		Size = pSprite->m_Size*OBJECT_SCALE;
			math::Vec2		P2 = BonePos + (Dir * Size);
			math::Vec2		PointA[4];
			math::Vec2		DirTL(-Dir.y, Dir.x); // can we assume Dir is already normalized?
			PointA[0]		= BonePos + (0.5f * Size.x * DirTL);
			PointA[1]		= PointA[0] + (Size.y * Dir);
			PointA[2]		= PointA[1] - (Size.x * DirTL);
			PointA[3]		= PointA[2] - (Size.y * Dir);

			if (math::Intersection::PointInsidePoly2D(P, PointA, 4))
			{
				return true;
			}
		}
#else
		if (!pBone->NoValidSprites())
		{
			math::Vec2	BonePos, Dir;
			GetBoneData(pBone, BonePos, Dir);

			for (unsigned int j=0; j<pBone->Sprites.size(); j++)
			{
				ui::UISprite*	pSprite = pBone->Sprites[j];
				if (!pSprite->IsValid()) continue;

				math::Vec2		Size = pSprite->m_Size*OBJECT_SCALE;
				math::Vec2		P2 = BonePos + (Dir * Size);

				// let's just try to manually construct out points
				math::Vec2		PointA[4];
				math::Vec2		DirTL(-Dir.y, Dir.x); // can we assume Dir is already normalized?
				PointA[0]		= BonePos + (0.5f * Size.x * DirTL);
				PointA[1]		= PointA[0] + (Size.y * Dir);
				PointA[2]		= PointA[1] - (Size.x * DirTL);
				PointA[3]		= PointA[2] - (Size.y * Dir);

				if (math::Intersection::PointInsidePoly2D(P, PointA, 4))
				{
					return true;
				}
			}
		}
#endif//BONES_SINGLE_LAYER

	}
	return false;
}


BipedInstance::BoneData*	BipedInstance::GetBoneByIdx(int BoneIdx)
{
	if (BoneIdx < 0 || BoneIdx >=(int)m_Bones.size()) return NULL;
	return m_Bones[BoneIdx];
}

BipedInstance::BoneData*	BipedInstance::GetBone(unsigned int ID)
{
	for (unsigned int i=0; i<m_Bones.size(); i++)
	{
		BoneData*	pBone = m_Bones[i];
		if (pBone->ID == ID) return pBone;
	}
	return NULL;
}
BipedInstance::BoneData*	BipedInstance::GetBone(const std::string& Name)
{
	BoneData* pData;
	for(int i = 0; i < (int)m_Bones.size(); i++)
	{
		pData = m_Bones[i];
	}
	return GetBone(num::CRC32::FastHash(Name));
}

bool	BipedInstance::IsVisible(const math::Vec2& Offset)
{
	if (!m_pAnimation) return false;
	if (m_Pos.x - m_pAnimation->Min.x - Offset.x > 1.0f ||
		m_Pos.y - m_pAnimation->Min.y - Offset.y> 1.0f ||
		m_Pos.x + m_pAnimation->Max.x - Offset.x < 0 || 
		m_Pos.y + m_pAnimation->Max.y - Offset.y < 0 ) return false;
	return true;
	
}

void	BipedInstance::RenderSubset(const math::Vec2& Offset, const std::vector<std::string>& Bones)
{
	BIPEDS_DRAWN++;		
	if (!m_pAnimation) return;
	for (unsigned int i=0; i<Bones.size(); i++)
	{	
		BoneData*	pBone = GetBone(Bones[i]);
		if (pBone)
		{
			RenderBone(pBone, Offset, math::Vec2(1,1));
		}
	}
}

void	BipedInstance::Render(const math::Vec2& Offset)
{
	BIPEDS_DRAWN++;		
	if (!m_pAnimation) return;
	for (unsigned int i=0; i<m_Bones.size(); i++)
	{
		RenderBone(m_Bones[i], Offset, math::Vec2(1,1));
	}
	if (DRAW_BONES)
	{
		// draw root
		math::Vec2	Size(GraphicsUtil::InvW*5, GraphicsUtil::InvH*5);
		dd::Manager::Instance().AddBox2D(GetPos()-Size, GetPos()+Size, ColorRGBA(255, 0, 0), true);
	}
}

void	BipedInstance::RenderShadow(const math::Vec2& Offset, const math::Vec2& Scale)
{
	if (!m_pAnimation) return;
	//for (unsigned int i=0; i<m_Bones.size(); i++)
	for (int i=(int)(m_Bones.size())-1; i>=0; i--)
	{
		RenderBone(m_Bones[i], Offset, Scale);
	}
}

void		BipedInstance::RenderDebug(const math::Vec2& Offset)
{
		//FontManager::Instance().Print(math::Vec2(0,0), "Scale +/- [%.0f%%]\n Speed up/dwn[%.0f FPS]\n Toggle Bones B\nToggle Flip F", OBJECT_SCALE*100.0f, FRAME_SPEED);

}

bool	BipedInstance::IsBoneVisible(BoneData* pBone)
{
	if (!pBone->pBone) return false;
	int	Frame = (int)m_AnimTime % (int)pBone->pBone->FrameC;
	return pBone->pBone->FrameA[Frame].TestFlag(BipedAnimFrame::FLAG_VISIBLE);
}

void	BipedInstance::GetBoneData(BoneData* pBone, math::Vec2& BonePos, math::Vec2& Dir)
{
	if (!pBone->pBone) return;

	pBone->pBone->GetBlendedFrameVec(m_AnimTime, BonePos, Dir, m_WrapAnimation);
	// are we xblending between animations as well?
	if (m_pBlendAnimation)
	{
		// get the bone
		bones::BipedBone*	pBlendBone = m_pBlendAnimation->GetBone(pBone->Name);
		if (pBlendBone)
		{
			math::Vec2	BPos, BDir;
			pBlendBone->GetBlendedFrameVec(m_BlendTime, BPos, BDir, m_WrapAnimation);
			float		t = m_BlendTime / XBLEND_TIME;
			BonePos = BonePos + ( (BPos-BonePos) * t);
			Dir		= Dir + ( (BDir-Dir) * (t));
		}
	}
	BonePos*=OBJECT_SCALE;	// debug visualization

	if (IsFlipped())
	{
		BonePos.x *=-1;
		Dir.x *=-1;
	} 
}
void		BipedInstance::RenderBone(BoneData* pBone,  const math::Vec2& Offset, const math::Vec2& Scale)
{
	if (!pBone) return;
	if (!pBone->IsVisible) return;
#ifdef BONES_SINGLE_LAYER
	if (!pBone->Sprite.IsValid() && !pBone->pAttachedBiped) return;
#else
	if (pBone->NoValidSprites() && !pBone->pAttachedBiped) return;
#endif

	if (!pBone->pBone) return;
	if (!IsBoneVisible(pBone)) 
		return;	// disabled

	BONES_DRAWN++;

	math::Vec2	BonePos, Dir, P;
	GetBoneData(pBone, BonePos, Dir);
	Dir.x *= GraphicsUtil::H2W;
	BonePos.x *=  GraphicsUtil::W2H;	// we've already pre-multipled the screen width into this one

	// apply the scale
	Dir.x *= Scale.x;
	Dir.y *= Scale.y;

	float	ShadowOffset = 1.0f;
	if (SHADOW_SCALE>0)
	{
		ShadowOffset = 1+(BonePos.y * SHADOW_SCALE);
		UTIL_CLAMP(ShadowOffset, 0, 1);

	}

	BonePos.x *=  Scale.x;
	BonePos.y *=  Scale.y;

	//	Dir.y *= GraphicsUtil::H2W;
//	BonePos.y *=  GraphicsUtil::H2W;

	if (pBone->pAttachedBiped)
	{
		math::Vec2	Off = pBone->AttachedBipedOffset;
		
		if (IsFlipped()) Off.x = -Off.x;

		pBone->pAttachedBiped->SetPos(m_Pos + BonePos + pBone->AttachedBipedOffset);
		pBone->pAttachedBiped->SetFlipped( (!m_ReverseAttachedBiped && IsFlipped() ) || (m_ReverseAttachedBiped&&!IsFlipped()) );
		pBone->pAttachedBiped->Render(Offset);
	}

	P = m_Pos -  Offset + BonePos ;

	if (DRAW_BONES)
	{
		math::Vec2			To = P + (Dir * pBone->pBone->Length*OBJECT_SCALE);
		dd::Manager::Instance().AddLine2D(P, To, ColorRGBA(255, 255, 0), false);
	}

	// render the sprite
	Graphics* pG = GetGraphics();
	Graphics::Vertex2D*	VertA = pG->GetTempVert();;

	if (IS_ADDITIVE || pBone->IsAdditive)
	{
		GetGraphics()->SetupAdditiveAlpha(true);
	}


#ifdef BONES_SINGLE_LAYER
	{
		ui::UISprite*	pSprite = &pBone->Sprite;

#else
	for (unsigned int i=0; i<pBone->Sprites.size(); i++)
	{
		ui::UISprite*	pSprite = pBone->Sprites[i];
		if (!pSprite->IsValid()) continue;
#endif

		if(pSprite && pSprite->GetImage())
		{
			Surface* pSurf = pSprite->GetImage()->GetSurface();
			if(pSurf)
			{
				res::ResourceFile::RES_STATUS Status = pSurf->GetState();
				if(Status == res::ResourceFile::RS_READY)
				{
					// ok
				}
				else
				{
					//something wrong
					//UIManager::Instance().AddToReCacheList(pSurf);
					return;

				}
			}
		}

		
		math::Vec2		P2 = P + (Dir * pSprite->m_Size.y*OBJECT_SCALE);
		math::Vec2		UV1, UV2;
		UV1 = pSprite->GetUV1();
		UV2 = pSprite->GetUV2();
		if (IsFlipped())
		{
			float f =UV1.x;
			UV1.x = UV2.x;
			UV2.x = f;
		}
		int		Count = GraphicsUtil::FillLine(P, P2, pSprite->m_Size.x*0.5f*(OBJECT_SCALE* (ShadowOffset)), OBJECT_COLOR, OBJECT_COLOR,  UV1, UV2, VertA);
		if (pSprite->GetImage())
		{
			//pSprite->SetTexture();
			pG->SetTexture(0, pSprite->GetImage());
		}
		pG->DrawVertex2D(VertA, Count);
	}
	if (IS_ADDITIVE || pBone->IsAdditive)
	{
		GetGraphics()->SetupAdditiveAlpha(false);
	}
}

bool	BipedInstance::SetAnimation(const std::string& Name, bool Force)
{
	if (!Force && strcmpi(Name.c_str(), m_CurrentAnim.c_str())==0) return false;	// already set
	m_pAnimation = bones::BipedAnimationSet::Instance().GetAnimation(vfs::VFS::GetBaseName(Name));
	m_CurrentAnim = Name;

	m_pBlendAnimation = 0;
	m_NextAnim.resize(0);
	m_AnimTime = m_BlendTime = 0;
	RefreshBones();

	return (m_pAnimation != NULL);
}
void	BipedInstance::ClearSprites()
{
	for (unsigned int i=0; i<m_Bones.size(); i++)
	{
		BoneData*	pData = m_Bones[i];
#ifndef BONES_SINGLE_LAYER
		for (unsigned int j=0; j<pData->Sprites.size(); j++)
		{
			delete pData->Sprites[j];
		}
		pData->Sprites.resize(0);
#endif
	}
}
bool	BipedInstance::LoadDefaults()
{
	int	SpritesLoaded = 0;
	if (ui::UISprite::NO_LOAD_IMAGE) return false;

	ClearSprites();

	for (unsigned int i=0; i<m_Bones.size(); i++)
	{
		BoneData*	pData = m_Bones[i];
		BipedBone*	pBone = pData->pBone;
		if (!pBone) continue;

		if (strlen(pBone->DefaultImg)>0)
		{
			if (pData->PostCat.empty())
			{
				pData->AddSprite(DEFAULT_PRECAT + pBone->DefaultImg+m_SpritePost);
			} else
			{
				pData->AddSprite(DEFAULT_PRECAT + pBone->DefaultImg+pData->PostCat);
			}

#ifdef BONES_SINGLE_LAYER
			if (pData->Sprite.IsValid())
				SpritesLoaded++;
#else
			if (!pData->Sprites.empty() && pData->Sprites[0]->IsValid())
				SpritesLoaded++;
#endif
		}

		pData->SortL = pBone->DefaultSortL;
		pData->SortR = pBone->DefaultSortR;
	}
	m_AnimTime = 0;
	ResortBones();
	// because the hidden attack bones is always valid.
	return SpritesLoaded > 2 || m_Bones.size() == SpritesLoaded;
}	

void		BipedInstance::SetBoneVisible(const std::string& Bone, bool IsVisible)
{
	BoneData* pBone = GetBone(Bone);
	if (pBone)
	{
		pBone->IsVisible = IsVisible;
	}
}

void	BipedInstance::SetBoneAdditive(const std::string& Bone, bool Additive)
{
	BoneData* pBone = GetBone(Bone);
	if (pBone)
	{
		pBone->IsAdditive = Additive;
	}
}

void	BipedInstance::AttachSprite(const std::string& Bone, const std::string& Sprite, int InsertIdx)
{
	BoneData* pBone = GetBone(Bone);
	if (pBone)
	{
		pBone->AddSprite(Sprite, InsertIdx);
	}
}

void	BipedInstance::ReplaceSprite(const std::string& Bone, const std::string& Sprite, int InsertIdx)
{
	BoneData* pBone = GetBone(Bone);
	if (pBone)
	{
		std::string	PostCat;
		if (pBone->PostCat.empty())
		{
			PostCat = m_SpritePost;
		}
		else
		{
			PostCat = pBone->PostCat;
		}
#ifdef BONES_SINGLE_LAYER
		pBone->Sprite.Create(DEFAULT_PRECAT + Sprite +PostCat);
#else
		if (pBone->NoValidSprites())
		{
			pBone->AddSprite(DEFAULT_PRECAT + Sprite +PostCat, InsertIdx);
			return;
		}
		if (InsertIdx >= 0 && InsertIdx < (int)pBone->Sprites.size())
		{	
			pBone->Sprites[InsertIdx]->Create(DEFAULT_PRECAT + Sprite +PostCat);
		} else
		{
			// we already know it's not empty;
			pBone->Sprites[0]->Create(DEFAULT_PRECAT + Sprite +PostCat);
		}
#endif//#ifdef BONES_SINGLE_LAYER

	}
}

int		BipedInstance::DeleteSprite(const std::string& Bone, const std::string& Sprite)
{
	if (BoneData* pBone = GetBone(Bone))
	{
#ifdef BONES_SINGLE_LAYER
		pBone->Sprite.Invalidate();
		return 0;
#else
		if (pBone->NoValidSprites()) return -1;
		for (int i = (int)(pBone->Sprites.size() - 1); i >= 0; i--)
		{
			if (Sprite.empty() || strstr(pBone->Sprites[i]->GetFilename().c_str(), Sprite.c_str()))
			{
				delete pBone->Sprites[i];
				pBone->Sprites.erase(pBone->Sprites.begin() + i);
				return i;
			}
		}
#endif
	}
	return -1;
}

void BipedInstance::BoneData::AddSprite(const std::string& SpriteName, int InsertIdx)
{
	if (ui::UISprite::NO_LOAD_IMAGE) return;
	if (Name.empty()) return;
	// do we assume that these are always valid spriteinstances?
#ifdef BONES_SINGLE_LAYER
	Sprite.Create(SpriteName);
#else
	ui::UISprite*	pSprite = new ui::UISprite();
	pSprite->Create(SpriteName);
	if (pSprite->IsValid())
	{
		if ((InsertIdx >= 0) && (InsertIdx < (int)Sprites.size()))
			Sprites.insert(Sprites.begin() + InsertIdx, pSprite);
		else
			Sprites.push_back(pSprite);	
	} else
	{
		delete pSprite;
	}
#endif //BONES_SINGLE_LAYER
}

bool	BipedInstance::LoadSprites(const std::string& File)
{
	int		SpritesLoaded = 0;
	if (ui::UISprite::NO_LOAD_IMAGE) return false;
	LuaPlus::LuaState*	pScript = ScriptHelper::CreateLuaState();
	if (!ScriptHelper::DoFile(*pScript, File))
	{
		_LOG(MSG_ERROR, "Unable to load " << File << "!");
		 LuaPlus::LuaState::Destroy( pScript );
		return false;
	} else
	{
		LuaPlus::LuaObject Info  = pScript->GetGlobal("Character");
		for (int i=0; i<Info.GetTableCount(); i++)
		{
			LuaPlus::LuaObject BoneInfo  = Info.GetByIndex(i+1);
			if (BoneInfo.IsTable())
			{
				std::string		Name = ScriptHelper::GetTableString(BoneInfo, "BodyPart");
				BoneData* pBone = GetBone(Name);
				if (!pBone) continue;

				std::string	Sprite = ScriptHelper::GetTableString(BoneInfo, "Sprite");
				if (!Sprite.empty())
				{
					pBone->AddSprite(Sprite+m_SpritePost);
					if (!pBone->NoValidSprites())
						SpritesLoaded++;
				}
				pBone->SortL = ScriptHelper::GetTableFloat(BoneInfo, "Sort", 0);
				pBone->SortR = ScriptHelper::GetTableFloat(BoneInfo, "SortFlipped", pBone->SortL);
			}
		}
		ResortBones();
	}
	LuaPlus::LuaState::Destroy( pScript );
	return SpritesLoaded != 0;
}

void	BipedInstance::SetBonePost(const std::string& Name, const std::string& Data)
{
	BoneData*	pBone = GetBone(Name);
	if (pBone)
	{
		pBone->PostCat = Data;
	}
}

void	BipedInstance::ResortBones()
{
	// sort it
	if (m_Flipped)
	{
		std::sort(m_Bones.begin(), m_Bones.end(), BoneData::SortFlippedCB);
	} else
	{
		std::sort(m_Bones.begin(), m_Bones.end(), BoneData::SortNormalCB);
	}
}

void	BipedInstance::RefreshBones()
{
	if (m_pAnimation)
	{
		for (int i=0; i<m_pAnimation->BoneC; i++)
		{
			bones::BipedBone*	pBipBone = &m_pAnimation->BoneA[i];
			BoneData*	pBone = GetBone(pBipBone->ID);
			if (!pBone)
			{	
				pBone = new BoneData;
				pBone->Name = pBipBone->Name;
				pBone->ID = pBipBone->ID;
				m_Bones.push_back(pBone);
			}
			pBone->pBone = pBipBone;
		}
	} else
	{
		for (unsigned int i=0; i<m_Bones.size(); i++)
		{
			BoneData*	pBone = m_Bones[i];
			pBone->pBone = NULL;
		}
	}
}

void	BipedInstance::AttachBiped(BipedInstance* pBiped, const std::string& BoneName, const math::Vec2& Offset)
{
	if (pBiped==this) return;	// infinite loop
	// ensure there's no double-booking
	DetachBiped(pBiped);

	BoneData* pBone = GetBone(BoneName);
	if (!pBone)	return;

	pBone->AttachedBipedOffset = Offset;
	pBone->pAttachedBiped = pBiped;
}

void	BipedInstance::DetachBiped(BipedInstance* pBiped)
{
	for (unsigned int i=0; i<m_Bones.size(); i++)
	{
		if (m_Bones[i]->pAttachedBiped == pBiped)
		{
			m_Bones[i]->pAttachedBiped = 0;
			return;
		}
	}
}


